var OLDPPositionMap = function ( params ) {
	// options
	this.options = OpenLayers.Util.extend(
		{
			formID:      '',
			divID:       '',
			iconUrl:     '',
			iconWidth:   12,
			iconHeight:  12,
			callback:    undefined,
			mode:        OLDPPositionMap.MODE_SHOW
		}, params
	);
	this.mapType  = OLDPPositionMap.MAP_TYPE_GOOGLE;
	this.markersa = [];
	
	// create map
	this.map = new OpenLayers.Map(this.options.divID, {controls:[]});
	
	// add map layer
	this.addMap();
	this.map.zoomToMaxExtent();
	
	// map icon
	var s = new OpenLayers.Size(this.options.iconWidth,this.options.iconHeight);
	var o = new OpenLayers.Pixel(-(s.w/2), -s.h);
	this.mapIcon = new OpenLayers.Icon(this.options.iconUrl,s,o);
	
	// onClick controller
	if( this.options.mode === OLDPPositionMap.MODE_SELECT ){
		var c = new OpenLayers.Control.DPClick({oldppm: this});
		this.map.addControl(c);
		c.activate();
		
		// panzoom controller
		this.map.addControl(new OpenLayers.Control.PanZoom());
		
		// show mousecontroller in the top right corner
		//this.map.addControl(new OpenLayers.Control.MouseToolbar(new OpenLayers.Pixel(jQuery('#'+this.options.divID).width()-70, 8), 'horizontal'));
		
		// position the controller outside the box
		//   so mousecontrol is available but the buttons aren't visible
		this.map.addControl(new OpenLayers.Control.MouseToolbar(new OpenLayers.Pixel(jQuery('#'+this.options.divID).width()+100, 8), 'horizontal'));
	}
};

// constants
OLDPPositionMap.MODE_SELECT = 0;
OLDPPositionMap.MODE_SHOW   = 1;
OLDPPositionMap.MAP_TYPE_GOOGLE  = 0;
OLDPPositionMap.MAP_TYPE_HEL     = 1;

// methods
OLDPPositionMap.prototype = {
	destroy: function (  ){
		this.map.destroy();
	},
	
	addMap: function (  ){
		// late binding
	},
	
	addPosition: function ( lon, lat, zoom ){
		// convert meters to degree coordinates
		var lonD = 0, latD = 0
			,lonM = 0, latM = 0
			, c = []
			, m = undefined;
		if( (/^\d+$/).test(lon) ){
			c = this.metersToDegrees(lat, lon);
			lonD = c[0];
			latD = c[1];
			lonM = lon;
			latM = lat;
		}else{
			c = this.degreesToMeters(lat, lon);
			lonD = lon;
			latD = lat;
			lonM = Math.round(c[0]);
			latM = Math.round(c[1]);
		}
		
		// remove previous if MODE_SELECT
		if( this.options.mode === OLDPPositionMap.MODE_SELECT ){
			this.removeLastPosition();
		}
		
		// add new position
		if( this.mapType == OLDPPositionMap.MAP_TYPE_HEL ){
			m = new OpenLayers.Marker(new OpenLayers.LonLat(lonM,latM),this.mapIcon.clone());
		}else{
			m = new OpenLayers.Marker(new OpenLayers.LonLat(lonD,latD),this.mapIcon.clone());
		}
		this.markers.addMarker(m);
		this.markersa.push(m);
		
		//  call callback
		if( this.options.mode === OLDPPositionMap.MODE_SELECT ){
			// callback is called always with degree coordinates
			this.options.callback(lonD, latD, zoom);
		}
	},
	
	removeLastPosition: function (  ){
		if( this.markersa.length > 0 ){
			this.markers.removeMarker(this.markersa.pop());
		}
	},
	
	centerMap: function ( zoom ){
		zoom = zoom ? parseInt(zoom) : -1;
		var b = this.markers.getDataExtent();
		var z = this.map.getZoomForExtent(b,true);
		this.map.setCenter(b.getCenterLonLat(), (zoom > 0 ? zoom : z));
	},
	
	// Coordinate conversion
	// return lon, lat
	degreesToMeters: function ( qx, qy ) {
		// Geodeettisisita EUREF:sta suorakulmaisiin
		xyetrs = this.geodToRec(qx,qy,'etrs35tm35fin');
		xetrs  = xyetrs[0];
		yetrs  = xyetrs[1];
		
		// MUUNNOS EUREF-FIN -> KKJ3
		xykkj3 = this.transformation(xetrs,yetrs, 1.000400370278070,0.000003936276960,132.117721846671,2999942.26771054);
		x_kkj3 = xykkj3[0];
		y_kkj3 = xykkj3[1];
		
		// KKJ3 x,y -> KKJ f,l
		flkkj3 = this.recToGeod(x_kkj3,y_kkj3,'kkj3');
		f_kkj3 = flkkj3[0];
		l_kkj3 = flkkj3[1];
		
		// KKJ f,l -> KKJ2 x,y
		xykkj2 = this.geodToRec(f_kkj3,l_kkj3,'kkj2');
		x_kkj2 = xykkj2[0];
		y_kkj2 = xykkj2[1];
		
		// MUUNNOS KKJ2 x,y -> HKI
		xyhki = this.transformation(x_kkj2,y_kkj2,0.999876344,0.013939673,-6618695.854,-2595589.408);
		x_hki = xyhki[0];
		y_hki = xyhki[1];
		
		return [y_hki, x_hki];
	},
	
	// return lon, lat
	metersToDegrees: function ( qx, qy ) {
		// MUUNNOS HKI -> KKJ2:een
		xykkj2 = this.transformation(qx,qy,0.999929322,-0.013940412,6654411.646,2503138.614);
		x_kkj2 = xykkj2[0];
		y_kkj2 = xykkj2[1];
		
		// KKJ -> fii ja lambda
		r2g         = this.recToGeod(x_kkj2,y_kkj2,"kkj2");
		fii_kkj2    = r2g[0];
		lambda_kkj2 = r2g[1];
		
		// KKJ2 Fii,lambda -> KKJ3 x,y
		g2r    = this.geodToRec(fii_kkj2,lambda_kkj2,"kkj3");
		x_kkj3 = g2r[0];
		y_kkj3 = g2r[1];
		
		// helmert-muunnos kkj3(ykj)->etrs-tm35fin
		xy3    = this.transformation(x_kkj3,y_kkj3,0.999599789931095,-0.00000393312692,-143.863950116063,-2998741.66008629);
		x_etrs = xy3[0];
		y_etrs = xy3[1];
		
		// EUREF-FIN -> fii ja lambda
		r2g         = this.recToGeod(x_etrs,y_etrs,"etrs35tm35fin");
		fii_etrs    = r2g[0];
		lambda_etrs = r2g[1];
		
		return [lambda_etrs, fii_etrs];
	},
	
	recToGeod: function ( north, east, sys ){
		var a = 0, f = 0, k0 = 0
			, lambda0 = 0, e0 = 0;
		
		if(sys=="kkj2"){
			a = 6378388;
			f = 1/297;
			k0 = 1;
			lambda0 = 24;
			e0 = 2500000;
		}else if (sys=="kkj3"){
			a = 6378388;
			f = 1/297;
			k0 = 1;
			lambda0 = 27;
			e0 = 3500000;
		}else if (sys=="wgs84"){
			a = 6378137;
			f = 1/298.257223563;
			k0 = 1;
			lambda0 = 27;
			e0 = 500000;
		}else if (sys=="etrs35tm35fin"){
			a = 6378137;
			f = 1/298.257222101;
			k0 = 0.9996;
			lambda0 = 27;
			e0 = 500000;
		}else{
			a = 6378137;
			f = 1/298.257222101;
			k0 = 1;
			lambda0 = 27;
			e0 = 500000;
		}
		
		n = f/(2-f);
		ec = Math.sqrt(2*f-Math.pow(f,2));
		a1 = ((1 + Math.pow(n,2)/4 + Math.pow(n,4)/64)*a)/(1 + n);
		h1 = (1/2)*n-(2/3)*(Math.pow(n,2))+(37/96)*(Math.pow(n,3))-(1/360)*(Math.pow(n,4));
		h2 = (1/48)*(Math.pow(n,2))+(1/15)*(Math.pow(n,3))-(437/1440)*(Math.pow(n,4));
		h3 = (17/480)*(Math.pow(n,3))-(37/840)*(Math.pow(n,4));
		h4 = (4397/161280)*(Math.pow(n,4));
		zeta = north/(a1*k0);
		eta  = (east-e0)/(a1*k0);
		
		zeta1p = h1*Math.sin(2*zeta)*this.cosh(2*eta);
		zeta2p = h2*Math.sin(4*zeta)*this.cosh(4*eta);
		zeta3p = h3*Math.sin(6*zeta)*this.cosh(6*eta);
		zeta4p = h4*Math.sin(8*zeta)*this.cosh(8*eta);
		eta1p = h1*Math.cos(2*zeta)*this.sinh(2*eta);
		eta2p = h2*Math.cos(4*zeta)*this.sinh(4*eta);
		eta3p = h3*Math.cos(6*zeta)*this.sinh(6*eta);
		eta4p = h4*Math.cos(8*zeta)*this.sinh(8*eta);
		zetap = zeta- (zeta1p + zeta2p + zeta3p + zeta4p);
		etap  = eta - (eta1p + eta2p + eta3p + eta4p);
		
		tempbeta=(2 / (Math.exp(etap) + Math.exp(-etap)))*Math.sin(zetap);
		beta = Math.asin(tempbeta);
		
		l = Math.asin(this.tanh(etap)/Math.cos(beta));
		
		q = Math.log(Math.tan(beta) + Math.sqrt(Math.tan(beta) * Math.tan(beta) + 1));
		qptemp1 = ((Math.exp(q) - Math.exp(-q)) / (Math.exp(q) + Math.exp(-q)))*ec;
		qp = q + ec* Math.log((1 + qptemp1) / (1 - qptemp1)) / 2;
		q2ptemp= ((Math.exp(qp) - Math.exp(-qp)) / (Math.exp(qp) + Math.exp(-qp)))*ec;
		q2p = q + ec*Math.log((1+q2ptemp) / (1-q2ptemp)) / 2;
		q3ptemp= ((Math.exp(q2p) - Math.exp(-q2p)) / (Math.exp(q2p) + Math.exp(-q2p)))*ec;
		q3p = q + ec*Math.log((1+q3ptemp) / (1-q3ptemp)) / 2;
		q4ptemp= ((Math.exp(q3p) - Math.exp(-q3p)) / (Math.exp(q3p) + Math.exp(-q3p)))*ec;
		q4p = q + ec*Math.log((1+q4ptemp) / (1-q4ptemp)) / 2;
		q5ptemp= ((Math.exp(q4p) - Math.exp(-q4p)) / (Math.exp(q4p) + Math.exp(-q4p)))*ec;
		q5p = q + ec*Math.log((1+q5ptemp) / (1-q5ptemp)) / 2;
		q6ptemp= ((Math.exp(q5p) - Math.exp(-q5p)) / (Math.exp(q5p) + Math.exp(-q5p)))*ec;
		q6p = q + ec*Math.log((1+q6ptemp) / (1-q6ptemp)) / 2;
		q7ptemp= ((Math.exp(q6p) - Math.exp(-q6p)) / (Math.exp(q6p) + Math.exp(-q6p)))*ec;
		q7p = q + ec*Math.log((1+q7ptemp) / (1-q7ptemp)) / 2;
		
		fi = Math.atan(this.sinh(q7p));
		
		lambda0 = this.deg2rad(lambda0);
		lambda=(lambda0 + l);
		
		return [this.rad2deg(fi), this.rad2deg(lambda)];
	},
	
	geodToRec: function ( fi, lambda, sys ) {
		var a = 0, f = 0, k0 = 0
			, lambda0 = 0, e0 = 0;
		
		if (sys=="kkj2"){
			a = 6378388;
			f = 1/297;
			k0 = 1;
			lambda0 = 24;
			e0 = 2500000;
		}else if (sys=="kkj3"){
			a = 6378388;
			f = 1/297;
			k0 = 1;
			lambda0 = 27;
			e0 = 3500000;
		}else if (sys=="wgs84"){
			a = 6378137;
			f = 1/298.257223563;
			k0 = 1;
			lambda0 = 27;
			e0 = 500000;
		}else if (sys=="etrs35tm35fin"){
			a = 6378137;
			f = 1/298.257222101;
			k0 = 0.9996;
			lambda0 = 27;
			e0 = 500000;
		}else{
			a = 6378137;
			f = 1/298.257222101;
			k0 = 1;
			lambda0 = 27;
			e0 = 500000;
		}
		
		n = f/(2-f);
		ec = Math.sqrt(2*f-Math.pow(f,2));
		a1 = ((1 + Math.pow(n,2)/4 + Math.pow(n,4)/64)*a)/(1 + n);
		
		fi=this.deg2rad(fi);
		lambda=this.deg2rad(lambda);
		
		qp=Math.log(Math.tan(fi)+Math.sqrt(Math.tan(fi)*Math.tan(fi)+1));
		
		q2p=Math.log((1+ec*Math.sin(fi))/(1-ec*Math.sin(fi)))/2;
		q=qp-ec*q2p;
		
		lambda0=this.deg2rad(lambda0);
		l=lambda - lambda0;
		
		beta=Math.atan(this.sinh(q));
		etap= Math.log((1 + Math.cos(beta)*Math.sin(l)) / (1 - Math.cos(beta)*Math.sin(l))) / 2;
		
		zetap=Math.asin(Math.sin(beta)/(1/this.cosh(etap)));
		h1p = (0.5*n)-(Math.pow(n,2)*2/3)+(Math.pow(n,3)*5/16)+(Math.pow(n,4)*41/180);
		h2p = (Math.pow(n,2)*13/48)-(Math.pow(n,3)*3/5)+(Math.pow(n,4)*557/1440);
		h3p = (Math.pow(n,3)*61/240)-(Math.pow(n,4)*103/140);
		h4p = (Math.pow(n,4))*(49561/161280);
		zeta1=h1p*Math.sin(2*zetap)*this.cosh(2*etap);
		zeta2=h2p*Math.sin(4*zetap)*this.cosh(4*etap);
		zeta3=h3p*Math.sin(6*zetap)*this.cosh(6*etap);
		zeta4=h4p*Math.sin(8*zetap)*this.cosh(8*etap);
		eta1=h1p*Math.cos(2*zetap)*this.sinh(2*etap);
		eta2=h2p*Math.cos(4*zetap)*this.sinh(4*etap);
		eta3=h3p*Math.cos(6*zetap)*this.sinh(6*etap);
		eta4=h4p*Math.cos(8*zetap)*this.sinh(8*etap);
		
		zeta=zetap+(zeta1+zeta2+zeta3+zeta4);
		eta =etap +(eta1+eta2+eta3+eta4);
		
		north=a1*zeta*k0;
		east=a1*eta*k0 + e0;
		
		return [north, east];
	},
	
	transformation: function ( x, y, Ap, Bp, x0, y0 ) {
		Np=x0+Ap*x-Bp*y;
		Ep=y0+Bp*x+Ap*y;
		return [Np, Ep];
	},
	
	// Math functions
	deg2rad: function (angle) {
		return (angle/180)*Math.PI;
	},
	
	rad2deg: function (angle) {
		return (angle/Math.PI) * 180;
	},
	
	cosh: function (arg) {
		return (Math.exp(arg) + Math.exp(-arg))/2;
	},
	
	sinh: function (arg) {
		return (Math.exp(arg) - Math.exp(-arg))/2;
	},
	
	tanh: function (arg) {
		return (Math.exp(arg) - Math.exp(-arg)) / (Math.exp(arg) + Math.exp(-arg));
	}
};